package com.ww.springboot.es2.app.search.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.ww.springboot.es2.app.search.service.IEsSearchToolSvc;
import com.ww.springboot.es2.dto.EsQueryDTO;
import com.ww.springboot.es2.utils.ElasticsearchJsonUtil;
import com.ww.springboot.es2.utils.ElasticsearchTemplateUtil;
import com.ww.springboot.es2.utils.JsonPointUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.Charsets;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.unit.TimeValue;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static java.util.stream.Collectors.joining;

/**
 * 描述：
 *
 * @author 🧑 ‍wanwei
 * @since 2025-07-30 15:53
 */
@Slf4j
@Service
@AllArgsConstructor
public class EsSearchToolSvcImpl implements IEsSearchToolSvc {

    @Override
    public void exportWithApi(HttpServletResponse response, EsQueryDTO esQueryDTO) throws IOException {
        String jsonStr = JSONUtil.toJsonStr(esQueryDTO.getQueryJson());
        NativeSearchQueryBuilder queryBuilder = ElasticsearchJsonUtil.fromJson(jsonStr);
        int page = 0;
        int size = 20;
        List<List<String>> list = new ArrayList<>();
        list.add(this.getHeadList(jsonStr));
        ElasticsearchRestTemplate elasticsearchRestTemplate = ElasticsearchTemplateUtil.build(esQueryDTO.getProperties());
        while (true) {
            queryBuilder.withPageable(PageRequest.of(page, size));
            SearchRequest searchRequest = ElasticsearchJsonUtil.getSearchRequest(queryBuilder.build(), esQueryDTO.getIndexName());
            SearchResponse searchResponse = elasticsearchRestTemplate.execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
            SearchDocumentResponse searchDocumentResponse = SearchDocumentResponse.from(searchResponse);
            if (ObjectUtil.isEmpty(searchDocumentResponse)) {
                break;
            }
            if (ObjectUtil.isEmpty(searchDocumentResponse.getSearchDocuments())) {
                break;
            }
            this.addData(page, searchDocumentResponse, list);
            log.info("exportWithApi total: {}, page: {}", searchDocumentResponse.getTotalHits(), page);
            page++;
        }
        this.exportData(response, list);
    }

    @Override
    public void exportWithApiAndScroll(HttpServletResponse response, EsQueryDTO esQueryDTO) throws IOException {
        String jsonStr = JSONUtil.toJsonStr(esQueryDTO.getQueryJson());
        NativeSearchQueryBuilder queryBuilder = ElasticsearchJsonUtil.fromJson(jsonStr);
        int page = 0;
        int size = 20;
        List<List<String>> list = new ArrayList<>();
        list.add(this.getHeadList(jsonStr));
        ElasticsearchRestTemplate elasticsearchRestTemplate = ElasticsearchTemplateUtil.build(esQueryDTO.getProperties());
        String scrollId = "";
        while (true) {
            if(ObjectUtil.isEmpty(scrollId)){
                queryBuilder.withPageable(PageRequest.of(page, size));
                SearchRequest searchRequest = ElasticsearchJsonUtil.getSearchRequest(queryBuilder.build(), esQueryDTO.getIndexName());
                searchRequest.scroll(TimeValue.timeValueMinutes(15L));
                SearchResponse searchResponse = elasticsearchRestTemplate.execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
                SearchDocumentResponse searchDocumentResponse = SearchDocumentResponse.from(searchResponse);
                if (ObjectUtil.isEmpty(searchDocumentResponse)) {
                    break;
                }
                if (ObjectUtil.isEmpty(searchDocumentResponse.getSearchDocuments())) {
                    break;
                }
                scrollId = searchResponse.getScrollId();
                this.addData(page, searchDocumentResponse, list);
                log.info("exportWithApi total: {}, page: {},scrollId: {}", searchDocumentResponse.getTotalHits(), page, scrollId);
            }else {
                SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
                searchScrollRequest.scroll(TimeValue.timeValueMinutes(15L));
                SearchResponse searchResponse = elasticsearchRestTemplate.execute(client -> client.scroll(searchScrollRequest, RequestOptions.DEFAULT));
                SearchDocumentResponse searchDocumentResponse = SearchDocumentResponse.from(searchResponse);
                if (ObjectUtil.isEmpty(searchDocumentResponse)) {
                    break;
                }
                if (ObjectUtil.isEmpty(searchDocumentResponse.getSearchDocuments())) {
                    break;
                }
                this.addData(page, searchDocumentResponse, list);
                log.info("exportWithApi total: {}, page: {},scrollId: {}", searchDocumentResponse.getTotalHits(), page, scrollId);
            }
            page++;
        }
        //清除快照
        if(ObjectUtil.isNotEmpty(scrollId)){
            ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.addScrollId(scrollId);
            elasticsearchRestTemplate.execute(client -> client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT));
        }
        this.exportData(response, list);
    }

    private static void addData(int page, SearchDocumentResponse searchDocumentResponse, List<List<String>> list) {
        if (page == 0 && list.size() == 0) {
            list.add(new ArrayList<>(searchDocumentResponse.getSearchDocuments().get(0).keySet()));
        }
        List<String> headList = list.get(0);
        for (SearchDocument searchDocument : searchDocumentResponse.getSearchDocuments()) {
            List<String> line = new ArrayList<>();
            for (String column : headList) {
                Object o = searchDocument.get(column);
                if (o instanceof List) {
                    line.add(((ArrayList<?>) o).stream().map(i->String.valueOf(i)).collect(joining(",")));
                } else {
                    line.add(Optional.ofNullable(o).map(Object::toString).orElse(""));
                }
            }
            list.add(line);
        }
    }

    @Override
    public void exportWithApiAndPit(HttpServletResponse response, EsQueryDTO esQueryDTO) throws IOException {
        String jsonStr = JSONUtil.toJsonStr(esQueryDTO.getQueryJson());
        NativeSearchQueryBuilder queryBuilder = ElasticsearchJsonUtil.fromJson(jsonStr);
        int page = 0;
        int size = 20;
        List<List<String>> list = new ArrayList<>();
        list.add(this.getHeadList(jsonStr));
        ElasticsearchRestTemplate elasticsearchRestTemplate = ElasticsearchTemplateUtil.build(esQueryDTO.getProperties());
        while (true) {


        }
    }

    @Override
    public void exportWithHttp(HttpServletResponse response, EsQueryDTO esQueryDTO) throws IOException {

    }

    private static void exportData(HttpServletResponse response, List<List<String>> list) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding(Charsets.UTF_8.name());
        String fileName = "ES-EXPORT_" + System.currentTimeMillis();
        String sheetName = "列表";
        fileName = URLEncoder.encode(fileName, Charsets.UTF_8.name());
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream()).sheet(sheetName).doWrite(list);
    }

    private static List<String> getHeadList(String esQueryDTO) {
        return (List<String>) JsonPointUtil.jsonPoint(esQueryDTO, "/_source");
    }
}
